Step 31: Schema Validation
Let me remind you of the Bookmark schema:
import mongoose from "mongoose";
const BookmarkSchema = new mongoose.Schema({
title: { type: String, required: true },
url: { type: String, required: true },
});
const Bookmark = mongoose.model("Bookmark", BookmarkSchema);
export default Bookmark;
So what will happen if we make a bookmark and fail to provide a title? Let’s try it! Update the test in tests/mode/Bookmark.test.js
and set title
to null
test("test constructor", async () => {
db.connect(process.env.DB_TEST_URI);
const title = null;
const url = faker.internet.url();
const bookmark = await Bookmark.create({ title, url });
expect(bookmark.title).toBe(title);
expect(bookmark.url).toBe(url);
expect(bookmark.id).toBeDefined();
await Bookmark.deleteMany({});
});
Run this test. You should see a validation error generated by mongoose! By the way, you can provide more detailed error messages. Update src/model/Bookmark.js
as follows:
import mongoose from "mongoose";
const BookmarkSchema = new mongoose.Schema({
title: { type: String, required: [true, "Each bookmark must have a title"] },
url: { type: String, required: [true, "Each bookmark must have a URL"] },
});
const Bookmark = mongoose.model("Bookmark", BookmarkSchema);
export default Bookmark;
We should test for erroneous inputs. Here is an example where we can do that.
test("test title is null", async () => {
try {
const title = null;
const url = faker.internet.url();
await Bookmark.create({ title, url });
} catch (err) {
expect(err).toBeDefined();
}
});
Update tests/mode/Bookmark.test.js
as follows to add various error handling tests:
import { describe, beforeAll, afterAll, expect, it } from "vitest";
import Bookmark from "../../src/model/Bookmark.js";
import { faker } from "@faker-js/faker";
import * as db from "../../src/data/db.js";
import * as dotenv from "dotenv";
dotenv.config();
describe("Test Bookmark Schema & Model", () => {
beforeAll(async () => {
db.connect(process.env.DB_TEST_URI);
await Bookmark.deleteMany({});
});
it("test create bookmark", async () => {
const title = faker.lorem.sentence();
const url = faker.internet.url();
const bookmark = await Bookmark.create({ title, url });
expect(bookmark.title).toBe(title);
expect(bookmark.url).toBe(url);
expect(bookmark.id).toBeDefined();
});
describe("test title is required", () => {
it("test title is null", async () => {
try {
const title = null;
const url = faker.internet.url();
await Bookmark.create({ title, url });
} catch (err) {
expect(err).toBeDefined();
}
});
it("test title is undefined", async () => {
try {
const title = undefined;
const url = faker.internet.url();
await Bookmark.create({ title, url });
} catch (err) {
expect(err).toBeDefined();
}
});
it("test title is empty", async () => {
try {
const title = "";
const url = faker.internet.url();
await Bookmark.create({ title, url });
} catch (err) {
expect(err).toBeDefined();
}
});
});
describe("test url is required", () => {
it("test url is null", async () => {
try {
const title = faker.lorem.sentence();
const url = "";
await Bookmark.create({ title, url });
} catch (err) {
expect(err).toBeDefined();
}
});
it("test url is undefined", async () => {
try {
const title = faker.lorem.sentence();
const url = "";
await Bookmark.create({ title, url });
} catch (err) {
expect(err).toBeDefined();
}
});
it("test url is empty", async () => {
try {
const title = faker.lorem.sentence();
const url = "";
await Bookmark.create({ title, url });
} catch (err) {
expect(err).toBeDefined();
}
});
});
afterAll(async () => {
await Bookmark.deleteMany({});
});
});
Run the tests in this file and make sure they all pass. Then, save and commit changes.